﻿using AutoMapper;
using IOA.MES.DataAccess.Entity;
using IOA.MES.DataAccess.VModel;
using IOA.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using IOA.MES.DataAccess.Enum;
using IOA.MES.Auth.Business;
using IOA.MES.Log.DataAccess.VModel;
using IOA.MES.Common;

namespace IOA.MES.Business
{
    /// <summary>
    /// 用户管理（错误代码：105001）
    /// </summary>
    public class UserMgr
    {
        private MESContext mesDb = new MESContext("MES");

        /// <summary>
        /// 获取用户令牌
        /// </summary>
        /// <param name="token">令牌</param>
        /// <returns>令牌对象</returns>
        public VUserToken GetToken(string token)
        {
            var entity = string.IsNullOrWhiteSpace(token) ? null : mesDb.UserTokens.FirstOrDefault(_ => _.Token == token && _.Valid);
            return Mapper.Map<VUserToken>(entity);
        }

        /// <summary>
        /// 根据令牌查询用户
        /// </summary>
        /// <param name="token">令牌</param>
        /// <returns>用户对象</returns>
        public VUser GetUserByToken(string token)
        {
            var entity = mesDb.UserTokens.FirstOrDefault(_ => _.Token == token && _.Valid);
            if (entity == null || entity.Expires < DateTime.Now)
            {
                return null;
            }
            return GetUser(entity.UserId);
        }

        /// <summary>
        /// 添加Token
        /// </summary>
        /// <param name="model">Token对象</param>
        public VUserToken AddToken(VUserToken model)
        {
            var exists = mesDb.UserTokens.AsNoTracking().Where(_ => _.UserId == model.UserId && _.Valid).ToList();
            exists.ForEach(_ =>
            {
                mesDb.UserTokens.Attach(_);
                _.Valid = false;
                _.ModifyBy = model.CreateBy;
                _.ModifyTime = DateTime.Now;
            });

            var entity = Mapper.Map<UserToken>(model);
            entity.CreateTime = DateTime.Now;
            mesDb.UserTokens.Add(entity);
            mesDb.SaveChanges();

            return Mapper.Map<VUserToken>(entity);
        }

        /// <summary>
        /// 查询用户列表
        /// </summary>
        /// <param name="userIds">用户ID列表</param>
        /// <returns>用户列表</returns>
        public List<VUser> QueryUsers(List<int> userIds)
        {
            if (!userIds.Any())
            {
                return new List<VUser>();
            }
            var entities = mesDb.Users.AsNoTracking().Where(_ => userIds.Contains(_.PkId) && _.Valid).ToList();
            return Mapper.Map<List<VUser>>(entities);
        }

        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="token">用户令牌</param>
        public void Logout(string token)
        {
            var entity = mesDb.UserTokens.AsNoTracking().FirstOrDefault(_ => _.Token == token && _.Valid);
            if (entity != null)
            {
                entity.Valid = false;
                entity.ModifyTime = DateTime.Now;
                mesDb.SaveChanges();
            }
        }

        /// <summary>
        /// 获取用户对象
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="userName">用户名</param>
        /// <returns>用户对象</returns>
        public VUser GetUser(int userId, string userName = "")
        {
            if (userId <= 0 && string.IsNullOrWhiteSpace(userName))
            {
                return null;
            }
            var entity = userId > 0 ? mesDb.Users.AsNoTracking().FirstOrDefault(_ => _.PkId == userId && _.Valid) : mesDb.Users.FirstOrDefault(_ => _.UserName == userName && _.Valid);
            var model = Mapper.Map<VUser>(entity);
            if (model == null)
            {
                return null;
            }

            var roles = (from userRole in mesDb.UserRoles.AsNoTracking().Where(_ => _.UserId == model.PkId && _.Valid)
                         join role in mesDb.Roles.AsNoTracking().Where(_ => _.Valid) on userRole.RoleId equals role.PkId
                         select role).ToList();
            var workshops = (from userWorkshop in mesDb.UserWorkshops.AsNoTracking().Where(_ => _.UserId == model.PkId && _.Valid)
                             join workshop in mesDb.Workshops.AsNoTracking().Where(_ => _.Valid) on userWorkshop.WorkshopCode equals workshop.WorkshopCode
                             select workshop).ToList();
            var machineTypes = mesDb.UserMachineTypes.AsNoTracking().Where(_ => _.UserId == model.PkId && _.Valid).Select(_ => _.MachineType).ToList();
            model.Roles = Mapper.Map<List<VRole>>(roles);
            model.Workshops = Mapper.Map<List<VWorkshop>>(workshops);
            model.Sites = machineTypes.Select(_ => new KeyValuePair<MachineType, string>(_, !Enum.IsDefined(typeof(MachineType), _) ? string.Empty : EnumConvert.ToDesc(_))).ToList();

            return model;
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="lang">语言</param>
        /// <param name="model">登录对象</param>
        public VUser Login(string userName, string password, string lang, VLoginHistory model, bool needToken = false)
        {
            var entity = mesDb.Users.FirstOrDefault(_ => _.UserName == userName && _.Valid);
            if (entity == null)
            {
                throw new MultiLanBizException(105001, userName);//用户名不存在
            }
            else if (!string.Equals(entity.Password, Md5Utils.Md5Encrypt($"https://mes.ioa.tech/pwdencripy_{password}")))
            {
                throw new MultiLanBizException(105002);//用户名或密码错误
            }

            else if (entity.Status != CommonStatus.Normal)
            {
                throw new MultiLanBizException(105003);//用户状态异常
            }
            entity.Language = lang;
            mesDb.SaveChanges();

            model.UserId = entity.PkId;
            model.CreateBy = entity.PkId;
            model.CreateTime = DateTime.Now;
            model.LoginTime = DateTime.Now;
            new LoginHistoryMgr().Add(model);

            var userModel = Mapper.Map<VUser>(entity);
            userModel.Roles = Mapper.Map<List<VRole>>((from role in mesDb.Roles
                                                       join userRole in mesDb.UserRoles on role.PkId equals userRole.RoleId
                                                       where userRole.UserId == entity.PkId && role.Valid && userRole.Valid
                                                       select role).ToList());
            userModel.Workshops = Mapper.Map<List<VWorkshop>>((from workshop in mesDb.Workshops
                                                               join userWorkshop in mesDb.UserWorkshops on workshop.WorkshopCode equals userWorkshop.WorkshopCode
                                                               where userWorkshop.UserId == entity.PkId && workshop.Valid && userWorkshop.Valid
                                                               select workshop).ToList());
            if (needToken)
            {
                var userToken = AddToken(new VUserToken
                {
                    UserId = entity.PkId,
                    Token = Guid.NewGuid().ToString(),
                    Expires = DateTime.Now.AddYears(1),
                    CreateBy = entity.PkId
                });
                userModel.Token = userToken.Token;
            }

            return userModel;
        }

        /// <summary>
        /// 编辑用户
        /// </summary>
        /// <param name="model">用户对象</param>
        public void EditUser(VUser model)
        {
            var roleIds = model.Roles.Select(_ => _.PkId).Distinct().ToList();
            var roles = mesDb.Roles.Where(_ => roleIds.Contains(_.PkId) && _.Valid).ToList();
            if (!roles.Any())
            {
                throw new MultiLanBizException(105004);// "请至少选择一个岗位");
            }

            var workshops = model.Workshops == null || !model.Workshops.Any() ? new List<VWorkshop>()
                : new WorkshopMgr().QueryWorkshops(model.Workshops.Select(_ => _.WorkshopCode.ToUpper()).Distinct().ToList());
            if (model.PkId <= 0)
            {
                var exist = mesDb.Users.FirstOrDefault(_ => _.UserName == model.UserName && _.Valid);
                if (exist != null)
                {
                    throw new MultiLanBizException(105005);// "登录名已存在，请修改其他登录名");
                }
                model.CreateTime = DateTime.Now;
                var entity = Mapper.Map<User>(model);
                mesDb.Users.Add(entity);
                mesDb.SaveChanges();

                var userRoles = model.Roles.Select(_ => new UserRole
                {
                    RoleId = _.PkId,
                    UserId = entity.PkId,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now,
                    DepartmentId = roles.FirstOrDefault(r => r.PkId == _.PkId)?.DepartmentId ?? 0
                }).ToList();
                var userWorkshops = model.Workshops.Select(_ => new UserWorkshop
                {
                    WorkshopCode = _.WorkshopCode,
                    WorkshopName = workshops.FirstOrDefault(w => StringUtils.Equals(_.WorkshopCode, w.WorkshopCode))?.WorkshopName,
                    UserId = entity.PkId,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now
                }).ToList();
                var userMachines = model.Sites.Select(_ => new UserMachineType
                {
                    UserId = entity.PkId,
                    MachineType = _.Key,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now
                }).ToList();
                mesDb.UserRoles.AddRange(userRoles);
                mesDb.UserWorkshops.AddRange(userWorkshops);
                mesDb.UserMachineTypes.AddRange(userMachines);
                mesDb.SaveChanges();
            }
            else
            {
                var entity = mesDb.Users.FirstOrDefault(_ => _.PkId == model.PkId && _.Valid);
                if (entity == null)
                {
                    throw new MultiLanBizException(105006);// "用户不存在或已被删除，不可编辑");
                }
                var exist = mesDb.Users.FirstOrDefault(_ => _.UserName == model.UserName && _.PkId != model.PkId && _.Valid);
                if (exist != null)
                {
                    throw new MultiLanBizException(105005);// "登录名已存在，请修改其他登录名");
                }
                entity.TrueName = model.TrueName;
                entity.Email = model.Email;
                entity.Phone = model.Phone;
                entity.ModifyBy = model.ModifyBy;
                entity.ModifyTime = DateTime.Now;
                mesDb.SaveChanges();

                var existUserRoles = mesDb.UserRoles.Where(_ => _.UserId == model.PkId && _.Valid).ToList();
                var removeUserRoles = existUserRoles.Where(r => !roleIds.Contains(r.RoleId)).ToList();
                var newUserRoles = model.Roles.Where(r => !existUserRoles.Select(_ => _.RoleId).Contains(r.PkId)).Select(_ => new UserRole
                {
                    RoleId = _.PkId,
                    UserId = entity.PkId,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now,
                    DepartmentId = roles.FirstOrDefault(r => r.PkId == _.PkId)?.DepartmentId ?? 0
                }).ToList();
                removeUserRoles.ForEach(_ =>
                {
                    _.Valid = false;
                    _.ModifyBy = model.ModifyBy;
                    _.ModifyTime = DateTime.Now;
                });
                mesDb.UserRoles.AddRange(newUserRoles);
                mesDb.SaveChanges();

                var existUserWorkshops = mesDb.UserWorkshops.Where(_ => _.UserId == model.PkId && _.Valid).ToList();
                var removeUserWorkshops = existUserWorkshops.Where(r => !model.Workshops.Select(_ => _.WorkshopCode).Contains(r.WorkshopCode)).ToList();
                var newUserWorkshops = model.Workshops.Where(r => !existUserWorkshops.Select(_ => _.WorkshopCode).Contains(r.WorkshopCode)).Select(_ => new UserWorkshop
                {
                    WorkshopCode = _.WorkshopCode,
                    WorkshopName = workshops.FirstOrDefault(w => StringUtils.Equals(_.WorkshopCode, w.WorkshopCode))?.WorkshopName,
                    UserId = entity.PkId,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now
                }).ToList();
                removeUserWorkshops.ForEach(_ =>
                {
                    _.Valid = false;
                    _.ModifyBy = model.ModifyBy;
                    _.ModifyTime = DateTime.Now;
                });
                mesDb.UserWorkshops.AddRange(newUserWorkshops);
                mesDb.SaveChanges();

                var existSites = mesDb.UserMachineTypes.Where(_ => _.UserId == model.PkId && _.Valid).ToList();
                var removeSites = existSites.Where(r => !model.Sites.Select(_ => _.Key).Contains(r.MachineType)).ToList();
                var newSites = model.Sites.Where(r => !existSites.Select(_ => _.MachineType).Contains(r.Key)).Select(_ => new UserMachineType
                {
                    MachineType = _.Key,
                    UserId = entity.PkId,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now
                }).ToList();
                removeSites.ForEach(_ =>
                {
                    _.Valid = false;
                    _.ModifyBy = model.ModifyBy;
                    _.ModifyTime = DateTime.Now;
                });
                mesDb.UserMachineTypes.AddRange(newSites);
                mesDb.SaveChanges();
            }
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="oldPwd">旧密码</param>
        /// <param name="newPwd">新密码</param>
        public void ModifyPassword(int userId, string oldPwd, string newPwd)
        {
            var entity = mesDb.Users.AsNoTracking().FirstOrDefault(_ => _.PkId == userId && _.Valid);
            if (entity == null)
            {
                throw new MultiLanBizException(105008);// "用户不存在或已被删除，无法重复删除");
            }
            else if (!string.Equals(entity.Password, Md5Utils.Md5Encrypt($"https://mes.ioa.tech/pwdencripy_{oldPwd}")))
            {
                throw new MultiLanBizException(105010);//旧密码错误
            }

            mesDb.Users.Attach(entity);
            entity.Password = Md5Utils.Md5Encrypt($"https://mes.ioa.tech/pwdencripy_{newPwd}");
            entity.ModifyBy = userId;
            entity.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 重置密码
        /// </summary>
        /// <param name="userId">用户ID</param>
        public void ResetPassword(int userId)
        {
            var entity = mesDb.Users.AsNoTracking().FirstOrDefault(_ => _.PkId == userId && _.Valid);
            if (entity == null)
            {
                throw new MultiLanBizException(105008);// "用户不存在或已被删除，无法重复删除");
            }

            mesDb.Users.Attach(entity);
            entity.Password = Md5Utils.Md5Encrypt($"https://mes.ioa.tech/pwdencripy_123456");
            entity.ModifyBy = userId;
            entity.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="model">用户对象</param>
        public void DeleteUser(VUser model)
        {
            var user = mesDb.Users.FirstOrDefault(_ => _.PkId == model.PkId && _.Valid);
            if (user == null)
            {
                throw new MultiLanBizException(105008);// "用户不存在或已被删除，无法重复删除");
            }
            user.Valid = false;
            user.ModifyBy = model.ModifyBy;
            user.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 修改用户状态
        /// </summary>
        /// <param name="model">用户对象</param>
        public void ModifyStatus(VUser model)
        {
            var user = mesDb.Users.FirstOrDefault(_ => _.PkId == model.PkId && _.Valid);
            if (user == null)
            {
                throw new MultiLanBizException(105009);// "用户不存在或已被删除，无法修改状态");
            }
            user.Status = model.Status;
            user.ModifyBy = model.ModifyBy;
            user.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 查询用户列表
        /// </summary>
        /// <param name="pageIndex">页面索引</param>
        /// <param name="pageSize">页面容量</param>
        /// <param name="total">全部数量</param>
        /// <param name="userDepartmentIds">部门ID列表</param>
        /// <param name="roleId">岗位ID</param>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="status">用户状态</param>
        /// <param name="key">关键词</param>
        /// <returns>用户列表</returns>
        public List<VUser> QueryUsers(int pageIndex, int pageSize, out int total, List<int> departmentIds, int roleId, List<string> workshopCodes, CommonStatus? status, string key)
        {
            var query = from user in mesDb.Users.AsNoTracking()
                        join userRole in mesDb.UserRoles.AsNoTracking() on user.PkId equals userRole.UserId
                        join userWorkshop in mesDb.UserWorkshops.AsNoTracking().Where(_ => _.Valid) on user.PkId equals userWorkshop.UserId into temp
                        from uw in temp.DefaultIfEmpty()
                        where user.Valid && userRole.Valid && (roleId <= 0 || userRole.RoleId == roleId) && departmentIds.Contains(userRole.DepartmentId)
                            && (uw == null || workshopCodes.Contains(uw.WorkshopCode))
                        select user;
            if (status.HasValue)
            {
                query = query.Where(_ => _.Status == status.Value);
            }
            if (!string.IsNullOrWhiteSpace(key))
            {
                query = query.Where(_ => _.UserName.Contains(key) || _.TrueName.Contains(key) || _.Phone.Contains(key) || _.Email.Contains(key));
            }
            query = query.Distinct();
            total = query.Count();
            query = query.OrderByDescending(_ => _.PkId);

            var entities = pageSize <= 0 ? query.ToList() : query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            var models = Mapper.Map<List<VUser>>(entities);

            var userIds = models.Select(_ => _.PkId);
            var userRoles = (from userRole in mesDb.UserRoles.AsNoTracking()
                             join role in mesDb.Roles.AsNoTracking() on userRole.RoleId equals role.PkId
                             where userRole.Valid && userIds.Contains(userRole.UserId) && role.Valid
                             select new
                             {
                                 userRole.UserId,
                                 userRole.RoleId,
                                 userRole.DepartmentId,
                                 role.RoleName
                             }).ToList();
            var userWorkshops = (from userWorkshop in mesDb.UserWorkshops.AsNoTracking()
                                 join workshop in mesDb.Workshops.AsNoTracking() on userWorkshop.WorkshopCode equals workshop.WorkshopCode
                                 where userWorkshop.Valid && userIds.Contains(userWorkshop.UserId) && workshop.Valid
                                 select new
                                 {
                                     userWorkshop.UserId,
                                     userWorkshop.WorkshopCode,
                                     workshop.WorkshopName
                                 }).ToList();
            var departments = mesDb.Departments.AsNoTracking().Where(_ => _.Valid).ToList();
            models.ForEach(user =>
            {
                user.Roles = userRoles.Where(_ => _.UserId == user.PkId)
                        .Select(_ => new VRole { PkId = _.RoleId, DepartmentId = _.DepartmentId, RoleName = _.RoleName }).ToList();
                user.Workshops = userWorkshops.Where(_ => _.UserId == user.PkId)
                        .Select(_ => new VWorkshop { WorkshopCode = _.WorkshopCode, WorkshopName = _.WorkshopName }).ToList();

                user.Roles.ForEach(role =>
                {
                    var departmentNames = new List<string>();
                    var parentId = role.DepartmentId;
                    while (true)
                    {
                        var curDepartment = departments.FirstOrDefault(_ => _.PkId == parentId);
                        if (curDepartment == null)
                        {
                            break;
                        }
                        parentId = curDepartment.ParentId;
                        departmentNames.Add(curDepartment.DepartmentName);
                    }
                    departmentNames.Reverse();
                    role.DepartmentName = string.Join(">", departmentNames);
                });
            });

            return models;
        }
    }
}
